package com.dearcocoa.config;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dearcocoa.common.Status;
import com.dearcocoa.common.UserPrincipal;
import com.dearcocoa.entity.Permission;
import com.dearcocoa.entity.RolePermission;
import com.dearcocoa.entity.UserRole;
import com.dearcocoa.exception.SecurityException;
import com.dearcocoa.mapper.PermissionMapper;
import com.dearcocoa.mapper.RoleMapper;
import com.dearcocoa.mapper.RolePermissionMapper;
import com.dearcocoa.mapper.UserRoleMapper;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 *  动态路由认证
 * </p>
 *
 * @Package: com.dearcocoa.config
 * @ClassName: RbacAuthorityService
 * @Author: shengong
 * @Date: Created in 2020-08-02 21:34
 * @Version: V1.0
 * @Description: TODO
 */
@Component
public class RbacAuthorityService {

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private RolePermissionMapper rolePermissionMapper;

    @Autowired
    private RequestMappingHandlerMapping mapping;

    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        checkRequest(request);
        Object userInfo = authentication.getPrincipal();
        boolean hasPermission = false;

        if (userInfo instanceof UserDetails) {
            UserPrincipal userPrincipal = (UserPrincipal) userInfo;
            Long userId = userPrincipal.getId();

            List<UserRole> userRoles = userRoleMapper.selectList(Wrappers.lambdaQuery(new UserRole().setUserId(userId)));
            List<Long> roleIds = userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList());
            List<RolePermission> rolePermissions = rolePermissionMapper.selectList(Wrappers.<RolePermission>lambdaQuery().in(RolePermission::getRoleId, roleIds));
            List<Long> permissionIds = rolePermissions.stream().map(RolePermission::getPermissionId).collect(Collectors.toList());
            List<Permission> permissions = permissionMapper.selectList(Wrappers.<Permission>lambdaQuery().in(Permission::getId, permissionIds));

            // 获取资源，前后端分离，所以过滤页面权限，只保留按钮权限
            List<Permission> btnPermissions = permissions.stream()
                    // 过滤页面权限
//                    .filter(permission -> Objects.equals(permission.getType(), Consts.BUTTON))
                    // 过滤url为空
                    .filter(permission -> StrUtil.isNotBlank(permission.getUrl()))
                    // 过滤method为空
                    .filter(permission -> StrUtil.isNotBlank(permission.getMethod()))
                    .collect(Collectors.toList());

            for (Permission btnPermission : btnPermissions) {
                AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher(btnPermission.getUrl(), btnPermission.getMethod());
                if (antPathRequestMatcher.matches(request)) {
                    hasPermission = true;
                    break;
                }

            }

        }else {
            return false;
        }

        return hasPermission;
    }

    private void checkRequest(HttpServletRequest request) {
        // 当前request的方法
        String currentMethod = request.getMethod();
        Multimap<String, String> urlMapping = allUrlMapping();
        for (String uri : urlMapping.keySet()) {
            // 通过 AntPathRequestMatcher 匹配 url
            // 可以通过 2 种方式创建 AntPathRequestMatcher
            // 1：new AntPathRequestMatcher(uri,method) 这种方式可以直接判断方法是否匹配，因为这里我们把 方法不匹配 自定义抛出，所以，我们使用第2种方式创建
            // 2：new AntPathRequestMatcher(uri) 这种方式不校验请求方法，只校验请求路径
            AntPathRequestMatcher antPathMatcher = new AntPathRequestMatcher(uri);
            if (antPathMatcher.matches(request)){
                if (!urlMapping.get(uri).contains(currentMethod)) {
                    throw new SecurityException(Status.HTTP_BAD_METHOD);
                }else {
                    return;
                }
            }
        }
        throw new SecurityException(Status.REQUEST_NOT_FOUND);
    }

    /**
     * 获取 所有URL Mapping，返回格式为{"/test":["GET","POST"],"/sys":["GET","DELETE"]}
     * @return
     */
    private Multimap<String, String> allUrlMapping() {
        Multimap<String, String> urlMapping = ArrayListMultimap.create();

        // 获取url与类和方法的对应信息
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = mapping.getHandlerMethods();

        handlerMethods.forEach((k, v) -> {
            // 获取当前 key 下的获取所有URL
            Set<String> url = k.getPatternsCondition()
                    .getPatterns();
            RequestMethodsRequestCondition method = k.getMethodsCondition();

            // 为每个URL添加所有的请求方法
            url.forEach(s -> urlMapping.putAll(s, method.getMethods()
                    .stream()
                    .map(Enum::toString)
                    .collect(Collectors.toList())));
        });

        return urlMapping;

    }

}
